import asyncio
import subprocess
from typing import Dict

from app.core.constants import ExecutionStatus
from app.core.task.executor.base_executor import BaseExecutor
from app.core.task.task_log import scheduler_logger
from app.schemas.job import SchedulerJob, RedisJobExecutionResult


class ScriptExecutor(BaseExecutor):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._running_processes: Dict[str, asyncio.subprocess.Process] = {}

    async def cancel_job(self, job_id: str) -> bool:
        if job_id in self._running_processes:
            process = self._running_processes[job_id]
            try:
                process.terminate()
                await process.wait()
                execution_result = self._create_execution_result(job_id)
                execution_result = self._update_execution_result(
                    execution_result, ExecutionStatus.CANCELLED, error=Exception("任务被手动取消")
                )
                await self.store.add_job_execution_log(job_id, execution_result)
                self._task_results[job_id] = execution_result
                return True
            except Exception as e:
                scheduler_logger.error(f"取消任务 {job_id} 失败: {str(e)}")
            finally:
                del self._running_processes[job_id]
        return False

    async def execute_job(self, job: SchedulerJob) -> RedisJobExecutionResult:
        job_id = job.id
        execution_result = self._create_execution_result(job_id)

        try:
            cmd = job.args[0] if job.args else ""
            if not cmd:
                raise ValueError("脚本命令不能为空")

            process = await asyncio.create_subprocess_shell(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            self._running_processes[job_id] = process

            try:
                stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=job.timeout)
                if process.returncode == 0:
                    result = stdout.decode().strip()
                    execution_result = self._update_execution_result(
                        execution_result, ExecutionStatus.SUCCESS, execution_result=result
                    )
                    scheduler_logger.info(f"任务 {job_id} 执行成功, 耗时: {execution_result.duration:.2f}秒")
                else:
                    error_msg = stderr.decode().strip()
                    execution_result = self._update_execution_result(
                        execution_result, ExecutionStatus.FAILED, error=Exception(f"脚本执行失败: {error_msg}")
                    )
                    scheduler_logger.error(f"任务 {job_id} 执行失败: {error_msg}")

            except asyncio.TimeoutError:
                process.terminate()
                await process.wait()
                execution_result = self._update_execution_result(
                    execution_result,
                    ExecutionStatus.TIMEOUT,
                    error=Exception(f"TimeoutError: 任务执行超时 (>{job.timeout}秒)"),
                )
                scheduler_logger.error(f"任务 {job_id} 执行超时 (>{job.timeout}秒)")

        except Exception as e:
            execution_result = self._update_execution_result(execution_result, ExecutionStatus.FAILED, error=e)
            scheduler_logger.error(f"任务 {job_id} 执行失败: {str(e)}")

        finally:
            if job_id in self._running_processes:
                del self._running_processes[job_id]
            await self.store.add_job_execution_log(job_id, execution_result)
            return execution_result
